﻿using System;
using System.Collections.Generic;
using System.Globalization;
using Engine;
using Game;
using GameEntitySystem;
using Mekiasm.Common;
using TemplatesDatabase;
using static Mekiasm.MekCraftingRecipe;
namespace Mekiasm
{
    public class ComponentEnergyMachine : Component, IInventory, Interface.IMekCraftableMachine, Interface.IMekConfigMachine, Interface.IMekGasMachine, Interface.IMekPowerMachine, Interface.IMekFluidMachine
    {
        public enum FuelType
        {
            None,//无
            Electric,//能量
            Fliud,//液态
            Gas,//气态
            Heat,//热量
            Dust//粉
        }

        public enum SlotType
        {
            IN = 1,
            OUT = 2,
            CRAFT = 4,
            RESULT = 8,
            REMAIN = 16,
            Charge_SlotOut = 32
        }

        public class Slot
        {
            public SlotType SlotType;
            public FuelType FuelType;
            public int Count;
            public int Value;
            public long FuelAmount;
            public long FuelCapacity;
            public int FuelValue;
            public void ResetFuel()
            {
                FuelAmount = 0;
                FuelValue = 0;
                FuelType = default;
            }

            public void SetSlotTypeFlag(SlotType slotType, bool exists)
            {
                ushort v = (ushort)SlotType;
                byte p = GetBitSize((ushort)slotType);
                ushort m = (ushort)(1 << p);
                if (exists)
                {
                    v |= m;
                }
                else
                {
                    v &= (ushort)~m;
                }
                SlotType = (SlotType)v;
            }
            private byte GetBitSize(ushort u)
            {
                byte i = 0;
                while ((u = (ushort)(u >> 1)) > 0) i++;
                return i;
            }
        }


        public ComponentBlockEntity BlockEntity;

        public virtual bool IsCrafting { get; set; }

        public virtual bool AllowChangeElectricMode { get; set; }

        public virtual bool AllowChangeFluidMode { get; set; }

        public virtual bool AllowChangeItemMode { get; set; }

        public virtual bool AllowChangeGasMode { get; set; }

        public virtual int CraftModeType { get; set; }

        public Dictionary<int, MachineFaceConfig> MachineFaceConfig = new Dictionary<int, MachineFaceConfig>();

        public int VisibleSlotsCount { get; set; }

        public int SlotsCount { get; set; }

        public int ActiveSlotIndex { get; set; }

        public virtual string[] ingredians { get; set; }

        public virtual string[] fuelIngredians { get; set; }

        public virtual FuelType[] fuelIngredianTypes { get; set; }

        public virtual MekRecipe CurrentRecipe { get; set; }

        public virtual float CraftProgress { get; set; }

        public virtual int CraftPowerUse { get; set; }

        public virtual float HeatLevel { get; set; }

        public virtual Interface.IDynamicCraft CraftDevice => null;

        public virtual bool CheckResultCapacity => true;

        public virtual long[] fuelIngrediansAmount { get; set; }

        public ComponentEnergyMachine Machine { get; set; }

        public long Power { get; set; }
        public long MaxPower { get; set; }

        public long OutputGasAmountLimit { get; set; }
        public long InputGasAmountLimit { get; set; }

        public long OutputPowerLimit { get; set; }
        public long InputPowerLimit { get; set; }

        public long OutputFluidAmountLimit { get; set; }
        public long InputFluidAmountLimit { get; set; }

        public int AnimateRenderSlot { get; set; }

        public Game.Random m_random = new Game.Random();

        public int PowerTransitLimit = 0;
        public int FluidTransitLimit = 0;
        public int GasTransitLimit = 0;

        public List<Slot> Slots = new List<Slot>();

        public override void Load(ValuesDictionary valuesDictionary, IdToEntityMap idToEntityMap)
        {
            if (Entity != null) BlockEntity = Entity.FindComponent<ComponentBlockEntity>();
            CraftModeType = valuesDictionary.GetValue("CraftModeType",CraftModeType);
            CraftProgress = valuesDictionary.GetValue("CraftProgress", CraftProgress);
            SlotsCount = valuesDictionary.GetValue("SlotsCount", SlotsCount);
            ValuesDictionary slotValues = valuesDictionary.GetValue("Slots",new ValuesDictionary());
            ValuesDictionary configValues = valuesDictionary.GetValue("Configs", new ValuesDictionary());
            for (int i = 0; i < SlotsCount; i++)
            {
                Slots.Add(new Slot());
            }

            for (int j = 0; j < slotValues.Count; j++)
            {
                ValuesDictionary value3 = slotValues.GetValue<ValuesDictionary>(j.ToString(CultureInfo.InvariantCulture));
                Slot slot = Slots[j];
                slot.Value = value3.GetValue("Value", slot.Value);
                slot.Count = value3.GetValue("Count", slot.Count);
                slot.FuelAmount = value3.GetValue("FuelAmount", slot.FuelAmount);
                slot.FuelValue = value3.GetValue("FuelValue", slot.FuelValue);
                slot.FuelCapacity = value3.GetValue("FuelCapacity", slot.FuelCapacity);
                slot.SlotType = value3.GetValue("SlotType", slot.SlotType);
                slot.FuelType = value3.GetValue("FuelType", slot.FuelType);
            }

            foreach (var item in configValues)
            {
                var v = item.Value as ValuesDictionary;
                MachineFaceConfig.Add(int.Parse(item.Key), new MachineFaceConfig()
                {
                    DeviceType = v.GetValue<MekBlock.DeviceType>("DeviceType"),
                    MachineMode = v.GetValue<MekBlock.MachineMode>("MachineMode")

                });

            }
            LoadPowerData(valuesDictionary);
            LoadFliudData(valuesDictionary);
            LoadGasData(valuesDictionary);
            Machine = this;
            if (CraftDevice != null)
            {
                int len = CraftDevice.CraftSize.X * CraftDevice.CraftSize.Y;
                ingredians = new string[len];
                fuelIngredians = new string[len];
                fuelIngrediansAmount = new long[len];
                fuelIngredianTypes = new FuelType[len];
                OnSlotChange();
            }
        }

        public override void Save(ValuesDictionary valuesDictionary, EntityToIdMap entityToIdMap)
        {
            if (CraftProgress != 0) valuesDictionary.SetValue("CraftProgress", CraftProgress);
            if (CraftModeType != 0) valuesDictionary.SetValue("CraftModeType", CraftModeType);
            if (MachineFaceConfig.Count > 0)
            {
                ValuesDictionary Configs = new ValuesDictionary();
                valuesDictionary.SetValue("Configs", Configs);
                foreach (var config in MachineFaceConfig)
                {
                    ValuesDictionary pairs = new ValuesDictionary();
                    Configs.SetValue(config.Key.ToString(), pairs);
                    pairs.SetValue("DeviceType", config.Value.DeviceType);
                    pairs.SetValue("MachineMode", config.Value.MachineMode);
                }
            }
            if (Slots.Count > 0)
            {
                ValuesDictionary SlotsValues = new ValuesDictionary();
                valuesDictionary.SetValue("Slots", SlotsValues);
                for (int i = 0; i < Slots.Count; i++)
                {
                    ValuesDictionary SlotsItem = new ValuesDictionary();
                    var slot = Slots[i];
                    if (slot.Value != 0) SlotsItem.SetValue("Value", slot.Value);
                    if (slot.Count != 0) SlotsItem.SetValue("Count", slot.Count);
                    if (slot.FuelAmount != 0) SlotsItem.SetValue("FuelAmount", slot.FuelAmount);
                    if (slot.FuelValue != 0) SlotsItem.SetValue("FuelValue", slot.FuelValue);
                    if (slot.FuelCapacity != 0) SlotsItem.SetValue("FuelCapacity", slot.FuelCapacity);
                    if (slot.FuelType != default) SlotsItem.SetValue("FuelType", slot.FuelType);
                    if (slot.SlotType != default) SlotsItem.SetValue("SlotType", slot.SlotType);
                    SlotsValues.SetValue(i.ToString(), SlotsItem);
                }
            }

            SavePowerData(valuesDictionary);
            SaveFliudData(valuesDictionary);
            SaveGasData(valuesDictionary);
        }

        public virtual void ProcessSlotItems(int slotIndex, int value, int count, int processCount, out int processedValue, out int processedCount)
        {
            int slotCount = GetSlotCount(slotIndex);
            int slotValue = GetSlotValue(slotIndex);
            if (slotCount > 0 && slotValue != 0)
            {
                SubsystemBlockBehavior[] blockBehaviors = base.Project.FindSubsystem<SubsystemBlockBehaviors>(throwOnError: true).GetBlockBehaviors(Terrain.ExtractContents(slotValue));
                foreach (SubsystemBlockBehavior subsystemBlockBehavior in blockBehaviors)
                {
                    int processInventoryItemCapacity = subsystemBlockBehavior.GetProcessInventoryItemCapacity(this, slotIndex, value);
                    if (processInventoryItemCapacity > 0)
                    {
                        subsystemBlockBehavior.ProcessInventoryItem(this, slotIndex, value, count, MathUtils.Min(processInventoryItemCapacity, processCount), out processedValue, out processedCount);
                        return;
                    }
                }
            }
            processedValue = value;
            processedCount = count;
        }

        public virtual int GetSlotValue(int slotIndex)
        {
            var slot = Slots[slotIndex];
            if (slotIndex >= 0 && slotIndex < Slots.Count)
            {
                if (slot.Count <= 0)
                {
                    return 0;
                }
                return slot.Value;
            }
            return 0;
        }

        public ComponentPlayer FindInteractingPlayer()
        {
            ComponentPlayer componentPlayer = Entity.FindComponent<ComponentPlayer>();
            if (componentPlayer == null)
            {
                ComponentBlockEntity componentBlockEntity = base.Entity.FindComponent<ComponentBlockEntity>();
                if (componentBlockEntity != null)
                {
                    Vector3 position = new Vector3(componentBlockEntity.Coordinates);
                    componentPlayer = base.Project.FindSubsystem<SubsystemPlayers>(throwOnError: true).FindNearestPlayer(position);
                }
            }

            return componentPlayer;
        }

        /// <summary>
        /// 当物品改变，生成一次配方
        /// </summary>
        public virtual void OnSlotChange()
        {
            if (CraftDevice == null) return;
            bool hasWp = false;
            for (int i = 0; i < CraftDevice.CraftSlotIndies.Length; i++)
            {
                var slot = Slots[CraftDevice.CraftSlotIndies[i]];
                int v = slot.Value;
                int fv = slot.FuelValue;
                if (slot.Count > 0)
                {
                    hasWp = true;
                    ingredians[i] = ILibrary.GetCraftId(v);
                }
                else ingredians[i] = null;
            }
            for (int i = 0; i < CraftDevice.FuelCraftSlotIndies.Length; i++)
            {
                var slot = Slots[CraftDevice.FuelCraftSlotIndies[i]];
                int v = slot.Value;
                int fv = slot.FuelValue;
                if (slot.FuelAmount > 0)
                {
                    hasWp = true;
                    fuelIngredians[i] = ILibrary.GetCraftId(fv);
                    fuelIngrediansAmount[i] = slot.FuelAmount;
                    fuelIngredianTypes[i] = slot.FuelType;
                }
                else fuelIngredians[i] = null;
            }

            if (hasWp)
            {
                int[] indies = MekRecipe.TransformRecipe(ingredians, CraftDevice.CraftSize);
                string[] tmp1 = new string[indies.Length];
                for (int i = 0; i < indies.Length; i++)
                {
                    var obj = indies[i];
                    if (obj != -1)
                    {
                        tmp1[i] = ingredians[obj];
                    }
                }

                indies = MekRecipe.TransformRecipe(fuelIngredians, CraftDevice.CraftSize);
                string[] tmp2 = new string[indies.Length];
                FuelType[] tmp3 = new FuelType[indies.Length];

                for (int i = 0; i < indies.Length; i++)
                {
                    var obj = indies[i];
                    if (obj != -1)
                    {
                        tmp2[i] = fuelIngredians[obj];
                        tmp3[i] = fuelIngredianTypes[obj];
                    }
                }
                CurrentRecipe = MatchMekRecipe(CraftDevice, tmp1, tmp2, tmp3, fuelIngrediansAmount, this, HeatLevel, FindInteractingPlayer());
                if (CurrentRecipe == null)
                {
                    indies = MekRecipe.TransformRecipe(ingredians, CraftDevice.CraftSize, true);
                    tmp1 = new string[indies.Length];
                    for (int i = 0; i < indies.Length; i++)
                    {
                        var obj = indies[i];
                        if (obj != -1)
                        {
                            tmp1[i] = ingredians[obj];
                        }
                    }

                    indies = MekRecipe.TransformRecipe(fuelIngredians, CraftDevice.CraftSize, true);
                    tmp2 = new string[indies.Length];
                    tmp3 = new FuelType[indies.Length];
                    for (int i = 0; i < indies.Length; i++)
                    {
                        var obj = indies[i];
                        if (obj != -1)
                        {
                            tmp2[i] = fuelIngredians[obj];
                            tmp3[i] = fuelIngredianTypes[obj];
                        }
                    }
                    CurrentRecipe = MatchMekRecipe(CraftDevice, tmp1, tmp2, tmp3, fuelIngrediansAmount, this, HeatLevel, FindInteractingPlayer());
                }
            }
            else CurrentRecipe = null;
        }

        public virtual void DoCraft()
        {
        }
        public virtual void DrawCraftPanel(CanvasWidget panel, MekRecipe mekRecipe)
        {
        }

        public virtual FuelType GetFuelType(int SlotIndex)
        {
            return Slots[SlotIndex].FuelType;
        }

        public virtual long GetFuelAmount(int SlotIndex)
        {
            return Slots[SlotIndex].FuelAmount;
        }

        public virtual long GetFuelValue(int SlotIndex)
        {
            return Slots[SlotIndex].FuelAmount;
        }

        public virtual int GetSlotCount(int slotIndex)
        {
            if (slotIndex >= 0 && slotIndex < Slots.Count)
            {
                return Slots[slotIndex].Count;
            }
            return 0;
        }

        public virtual int GetSlotCapacity(int slotIndex, int value)
        {
            if (slotIndex >= 0 && slotIndex < Slots.Count)
            {
                return BlocksManager.Blocks[Terrain.ExtractContents(value)].GetMaxStacking(value);
            }
            return 0;
        }

        public virtual bool AquireFuel(int slotIndex, int fuelValue, int Amount)
        {
            var slot = Slots[slotIndex];
            if (slot.FuelValue == 0 || slot.FuelValue == fuelValue)
            {
                if (CheckResultCapacity && slot.FuelAmount + Amount > slot.FuelCapacity) return false;
                return true;
            }
            else return false;
        }

        public virtual bool AquireItems(int slotIndex, int value, int count)
        {
            var slot = Slots[slotIndex];
            if (slot.Count == 0 || slot.Value == value)
            {
                if (CheckResultCapacity && slot.Count + count > BlocksManager.Blocks[Terrain.ExtractContents(slot.Value)].GetMaxStacking(slot.Value)) return false;
                return true;
            }
            else return false;
        }

        public virtual int GetSlotProcessCapacity(int slotIndex, int value)
        {
            int slotCount = GetSlotCount(slotIndex);
            int slotValue = GetSlotValue(slotIndex);
            if (slotCount > 0 && slotValue != 0)
            {
                SubsystemBlockBehavior[] blockBehaviors = base.Project.FindSubsystem<SubsystemBlockBehaviors>(throwOnError: true).GetBlockBehaviors(Terrain.ExtractContents(slotValue));
                for (int i = 0; i < blockBehaviors.Length; i++)
                {
                    int processInventoryItemCapacity = blockBehaviors[i].GetProcessInventoryItemCapacity(this, slotIndex, value);
                    if (processInventoryItemCapacity > 0)
                    {
                        return processInventoryItemCapacity;
                    }
                }
            }
            return 0;
        }

        public virtual void AddSlotItems(int slotIndex, int value, int count)
        {
            if (count > 0 && slotIndex >= 0 && slotIndex < Slots.Count)
            {
                Slot slot = Slots[slotIndex];
                if ((GetSlotCount(slotIndex) != 0 && GetSlotValue(slotIndex) != value) || GetSlotCount(slotIndex) + count > GetSlotCapacity(slotIndex, value))
                {
                    throw new InvalidOperationException("Cannot add slot items.");
                }
                slot.Value = value;
                slot.Count += count;
                OnSlotChange();
            }
        }

        public virtual int RemoveSlotItems(int slotIndex, int count)
        {
            if (slotIndex >= 0 && slotIndex < Slots.Count)
            {
                Slot slot = Slots[slotIndex];
                count = MathUtils.Min(count, GetSlotCount(slotIndex));
                slot.Count -= count;
                OnSlotChange();
                return count;
            }
            return 0;
        }

        public void DropAllItems(Vector3 position)
        {
            for (int i = 0; i < SlotsCount; i++)
            {
                ComponentInventoryBase.DropSlotItems(this, i, position, m_random.Float(5f, 10f) * Vector3.Normalize(new Vector3(m_random.Float(-1f, 1f), m_random.Float(1f, 2f), m_random.Float(-1f, 1f))));
            }
        }

        public virtual SlotType GetSlotType(int slotIndex)
        {
            return Slots[slotIndex].SlotType;
        }

        public bool AddFuel(int slotIndex, FuelType fuelType, long Amount, int value)
        {
            var slot = Slots[slotIndex];
            if (slot.FuelType != FuelType.None && slot.FuelType != fuelType) return false;
            else if (slot.FuelAmount > 0 && slot.FuelValue != value) return false;
            else if (slot.FuelAmount + Amount > slot.FuelCapacity) return false;
            else
            {
                slot.FuelAmount += Amount;
                slot.FuelType = fuelType;
                slot.FuelValue = value;
                return true;
            }
        }

        public void RemoveFuel(int slotIndex, int Amount)
        {
            var slot = Slots[slotIndex];
            if (slot.FuelAmount - Amount <= 0)
            {
                slot.FuelAmount = 0;
                slot.FuelType = FuelType.None;
            }
            else
            {
                slot.FuelAmount -= Amount;
            }
            if (slot.FuelAmount == 0) OnSlotChange();
        }

        public void LoadGasData(ValuesDictionary valuePairs)
        {
            InputGasAmountLimit = valuePairs.GetValue("InputGasAmountLimit", InputGasAmountLimit);
            OutputGasAmountLimit = valuePairs.GetValue("OutputGasAmountLimit", OutputGasAmountLimit);
        }

        public void SaveGasData(ValuesDictionary valuePairs)
        {
            if (InputGasAmountLimit != 0) valuePairs.SetValue("InputGasAmountLimit", InputGasAmountLimit);
            if (OutputGasAmountLimit != 0) valuePairs.SetValue("OutputGasAmountLimit", OutputGasAmountLimit);
        }

        public void SavePowerData(ValuesDictionary valuePairs)
        {
            if (Power != 0) valuePairs.SetValue("Power", Power);
            if (MaxPower != 0) valuePairs.SetValue("MaxPower", MaxPower);
            if (InputPowerLimit != 0) valuePairs.SetValue("InputPowerLimit", InputPowerLimit);
            if (OutputPowerLimit != 0) valuePairs.SetValue("OutputPowerLimit", OutputPowerLimit);
        }

        public void LoadPowerData(ValuesDictionary valuePairs)
        {
            Power = valuePairs.GetValue("Power", Power);
            MaxPower = valuePairs.GetValue("MaxPower", MaxPower);
            InputPowerLimit = valuePairs.GetValue("InputPowerLimit", InputPowerLimit);
            OutputPowerLimit = valuePairs.GetValue("OutputPowerLimit", OutputPowerLimit);
        }

        public void SaveFliudData(ValuesDictionary valuePairs)
        {
            if (InputFluidAmountLimit != 0) valuePairs.SetValue("InputFluidAmountLimit", InputFluidAmountLimit);
            if (OutputFluidAmountLimit != 0) valuePairs.SetValue("OutputFluidAmountLimit", OutputFluidAmountLimit);
        }

        public void LoadFliudData(ValuesDictionary valuePairs)
        {
            InputFluidAmountLimit = valuePairs.GetValue("InputFluidAmountLimit", InputFluidAmountLimit);
            OutputFluidAmountLimit = valuePairs.GetValue("OutputFluidAmountLimit", OutputFluidAmountLimit);

        }

        public virtual bool CanAcceptFuel(int slotIndex, int fuelValue)
        {
            return false;
        }
    }
}
